package com.testdocs.client.common.tree;

import java.util.HashMap;
import java.util.Map;

import com.google.gwt.event.logical.shared.OpenEvent;
import com.google.gwt.event.logical.shared.OpenHandler;
import com.google.gwt.event.logical.shared.SelectionEvent;
import com.google.gwt.event.logical.shared.SelectionHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.user.client.ui.TreeItem;

public class DataTree<T> extends Composite {
	private Tree tree = new Tree();
	private Map<T, TreeItem> itemsMap = new HashMap<T, TreeItem>();
	private String loadingElement = "Loading";

	public DataTree() {
		initWidget(tree);
	}

	public void add(T parentNodeData, T newNodeData, String name, boolean hasSubGroups) {
		if (parentNodeData == null || itemsMap.containsKey(parentNodeData)) {
			TreeItem newItem = new TreeItem();
			newItem.setText(name);
			newItem.setUserObject(newNodeData);
			if (hasSubGroups) {
				addLoadingItem(newItem);
			}
			if (parentNodeData == null) {
				tree.addItem(newItem);
			} else {
				itemsMap.get(parentNodeData).addItem(newItem);				
			}
			itemsMap.put(newNodeData, newItem);
		}
	}
	
	public void move(T nodeData, T newParentNodeData) {
		if (itemsMap.containsKey(nodeData) && itemsMap.containsKey(newParentNodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			TreeItem newParentItem = itemsMap.get(newParentNodeData);
			item.remove();
			newParentItem.addItem(item);
		}
	}
	
	public void moveUp() {
		//TODO
	}
	
	public void moveDown() {
		//TODO
	}
	
	public void addSelectionHandler(final ActionHandler<T> handler) {
		tree.addSelectionHandler(new SelectionHandler<TreeItem>() {
			@SuppressWarnings("unchecked")
			@Override
			public void onSelection(SelectionEvent<TreeItem> event) {
				handler.handle((T)event.getSelectedItem().getUserObject());
			}
		});
	}
	
	public void addOpenHandler(final ActionHandler<T> handler) {
		tree.addOpenHandler(new OpenHandler<TreeItem>() {
			@SuppressWarnings("unchecked")
			@Override
			public void onOpen(OpenEvent<TreeItem> event) {
					handler.handle((T)event.getTarget().getUserObject());
			}
		});
	}
	
	public void remove(T nodeData) {
		if (itemsMap.containsKey(nodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			item.remove();
		}
	}
	
	public void removeAllSubElements(T nodeData) {
		if (itemsMap.containsKey(nodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			item.removeItems();
		}
	}
	
	public void rename(T nodeData, String newLabel) {
		if (itemsMap.containsKey(nodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			item.setText(newLabel);
		}
	}
	
	public void update(T parentNodeData, T nodeData, String name, boolean hasSubGroups) {
		if (itemsMap.containsKey(nodeData)) {
			TreeItem parentItem = itemsMap.get(parentNodeData);
			TreeItem item = itemsMap.get(nodeData);
			item.setText(name);
			if (item.getChildCount() == 0 && hasSubGroups) {
				addLoadingItem(item);
			}
			parentItem.addItem(item);
		} else {
			add(parentNodeData, nodeData, name, hasSubGroups);
		}
	}
	
	public boolean needLoad(T nodeData) {
		if (itemsMap.containsKey(nodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			return loadingElement.equals(item.getChild(0).getUserObject());
		}
		return false;
	}
	
	public boolean elementPresents(T nodeData) {
		return itemsMap.containsKey(nodeData);
	}
	
	public abstract static class ActionHandler<T> {
		public abstract void handle(T nodeData);
	}
	
	public void setSelected(T nodeData) {
		if (nodeData == null) {
			tree.setSelectedItem(null);
		} else if (itemsMap.containsKey(nodeData)) {
			TreeItem item = itemsMap.get(nodeData);
			tree.setSelectedItem(item, false);
			//item.setSelected(true);
			TreeItem current = item.getParentItem();
			while (current != null) {
				current.setState(true, false);
				current = current.getParentItem();
			}
		}		
	}
	
	private void addLoadingItem(TreeItem item) {
		TreeItem loadingItem = new TreeItem("Loading...");
		loadingItem.setUserObject(loadingElement);
		item.addItem(loadingItem);
	}
	
	public boolean isRootLoaded() {
		return tree.getItemCount() > 0;
	}
	
	public void clearAll() {
		tree.clear();
	}
}
